/**
 * Title :	jFamilyTree generator
 * Description: jFamilyTree generator
 * Version:	provider
 * Copyright (c) 2001-2008
 * Terms of use:see license agreement at http://treebolic.sourceforge.net/en/license.htm
 * Author: Bernard Bou
 * Company: bsys
 */
package jFamilyTree.site;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.Date;
import java.util.Observable;

import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;

import org.w3c.dom.Document;

import jFamilyTree.Messages;
import jFamilyTree.dtd.Dtd;
import jFamilyTree.generator.Pair;
import bsys.xml.transformer.DomTransformer;

/**
 * Make site
 * 
 * @author Bernard Bou
 */
public abstract class SiteMaker extends Observable
{
	// reference

	/**
	 * Jar file to transfer
	 */
	static private final String theJarFile = "jFamilyTreeAppletDom.jar"; //$NON-NLS-1$

	/**
	 * Dtd file to transfer
	 */
	static private final String theDTDFile = "jFamilyTree.dtd"; //$NON-NLS-1$

	/**
	 * Applet class
	 */
	static private final String theAppclass = "jFamilyTree.applet.jFamilyTree.class"; //$NON-NLS-1$

	/**
	 * HTML template file
	 */
	static public String theTemplateFile = "template.html"; //$NON-NLS-1$

	// progress steps

	/**
	 * Start
	 */
	static final int PROGRESSZERO = 0;

	/**
	 * Connect start
	 */
	static final int PROGRESSCONNECT = 0;

	/**
	 * Connect completed
	 */
	static final int PROGRESSCONNECTCOMPLETE = 10;

	/**
	 * Transfer XML
	 */
	static final int PROGRESSXFERXML = 10;

	/**
	 * Transfer HTML
	 */
	static final int PROGRESSXFERHTML = 40;

	/**
	 * Transfer applet Jar
	 */
	static final int PROGRESSXFERJAR = 50;

	/**
	 * Transfer DTD
	 */
	static final int PROGRESSXFERDTD = 75;

	/**
	 * Transfer images
	 */
	static final int PROGRESSXFERIMAGES = 80;

	/**
	 * Disconnect start
	 */
	static final int PROGRESSDISCONNECT = 95;

	/**
	 * Disconnect complete
	 */
	static final int PROGRESSCOMPLETE = 100;

	// D A T A

	/**
	 * Document (source)
	 */
	private Document theDocument;

	/**
	 * Repository (source) for templetes
	 */
	private String theRepository;

	/**
	 * Image repository (source)
	 */
	private String theImageRepository;

	/**
	 * Title for HTML title (target)
	 */
	private String theTitle;

	/**
	 * HTML file (target)
	 */
	protected String theHtmlFile;

	/**
	 * XML file (target)
	 */
	protected String theXmlFile;

	/**
	 * Operation string
	 */
	protected String theOperation;

	// C O N S T R U C T I O N

	/**
	 * Constructor
	 * 
	 * @param thisDocument
	 *        document (source)
	 * @param thisRepository
	 *        repository (source)
	 * @param thisImageRepository
	 *        image repository (source)
	 * @param thisHtmlFile
	 *        HTML file (target)
	 * @param thisXmlFile
	 *        XML file (target)
	 * @param thisTitle
	 *        HTML file title (target)
	 * @throws URISyntaxException
	 * @throws IOException
	 */
	public SiteMaker(Document thisDocument, String thisRepository, String thisImageRepository, String thisHtmlFile, String thisXmlFile, String thisTitle) throws URISyntaxException, IOException
	{
		theDocument = thisDocument;
		theTitle = thisTitle;
		theXmlFile = thisXmlFile;
		theHtmlFile = thisHtmlFile;
		theRepository = thisRepository;
		theImageRepository = thisImageRepository;
	}

	// I N T E R F A C E

	/**
	 * Connect to destination
	 * 
	 * @return true if successful
	 */
	abstract boolean connect();

	/**
	 * Disconnect from destination
	 * 
	 * @return true if successful
	 */
	abstract boolean disconnect();

	/**
	 * Change folder
	 * 
	 * @param thisRelativeFolder
	 *        relative folder
	 * @return true if successful
	 * @throws MalformedURLException 
	 * @throws URISyntaxException 
	 */
	abstract boolean changeFolder(String thisRelativeFolder) throws MalformedURLException, URISyntaxException;

	/**
	 * Make output stream
	 * 
	 * @param thisFilename
	 *        filename
	 * @return output stream
	 * @throws MalformedURLException
	 * @throws IOException
	 * @throws URISyntaxException 
	 */
	abstract OutputStream makeOutStream(String thisFilename) throws MalformedURLException, IOException, URISyntaxException;

	// O B S E R V A B L E

	/**
	 * Notify operation progress
	 * 
	 * @param thisOperation
	 *        pending operation string
	 * @param thisProgressValue
	 *        progress value (max 100)
	 */
	void notifyOperation(String thisOperation, int thisProgressValue)
	{
		setChanged();
		theOperation = thisOperation;
		notifyObservers(new Pair<String, Integer>(thisOperation, thisProgressValue));
	}

	// O P E R A T I O N

	/**
	 * Make site operation
	 * 
	 * @return true if successful
	 */
	public boolean make()
	{
		// connect
		notifyOperation(Messages.getString("SiteMaker.10"), PROGRESSCONNECT); //$NON-NLS-1$
		if (!connect())
		{
			notifyOperation(Messages.getString("SiteMaker.11"), PROGRESSCOMPLETE); //$NON-NLS-1$
			return false;
		}

		boolean success = false;
		try
		{
			// urls
			URL thisRepositoryUrl = new URL(theRepository);
			URL thisImageRepositoryUrl = new URL(theImageRepository);

			// xml file
			notifyOperation("STOR " + theXmlFile, PROGRESSXFERXML); //$NON-NLS-1$
			OutputStream thisOutputStream = makeOutStream(theXmlFile);
			new DomTransformer().documentToStream(theDocument, thisOutputStream);
			thisOutputStream.close();

			// html file
			notifyOperation("STOR " + theHtmlFile, PROGRESSXFERHTML); //$NON-NLS-1$
			String[] theseMacros = { "%XMLFILE%", "%TITLE%", "%CLASS%", "%JAR%", "%WIDTH%", "%HEIGHT%", "%DATE%" }; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$
			String[] theseValues = { theXmlFile, theTitle, theAppclass, theJarFile, "100%", "100%", new Date().toString() }; //$NON-NLS-1$ //$NON-NLS-2$
			URL thisUrl = new URL(thisRepositoryUrl, theTemplateFile);
			thisOutputStream = makeOutStream(theHtmlFile);
			copyStreamsReplace(thisUrl.openStream(), thisOutputStream, theseMacros, theseValues);
			thisOutputStream.close();

			// copy code jar
			notifyOperation("STOR " + theJarFile, PROGRESSXFERJAR); //$NON-NLS-1$
			thisUrl = new URL(thisRepositoryUrl, theJarFile);
			thisOutputStream = makeOutStream(theJarFile);			
			copyStreams(thisUrl.openStream(), thisOutputStream);
			thisOutputStream.close();

			// copy dtd
			notifyOperation("STOR " + theDTDFile, PROGRESSXFERDTD); //$NON-NLS-1$
			thisOutputStream = makeOutStream(theDTDFile);			
			Dtd.copyToUTF8Stream(thisOutputStream);
			thisOutputStream.close();

			// copy images
			notifyOperation("CD " + "images", PROGRESSXFERIMAGES); //$NON-NLS-1$ //$NON-NLS-2$
			if (changeFolder("images")) //$NON-NLS-1$
			{
				for (String thisImageFileName : DocumentSearch.makeImageList(theDocument))
				{
					notifyOperation("STOR " + "images" + "/" + thisImageFileName, PROGRESSXFERIMAGES); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
					thisUrl = new URL(thisImageRepositoryUrl, thisImageFileName);
					thisOutputStream = makeOutStream(thisImageFileName);		
					copyStreams(thisUrl.openStream(), thisOutputStream);
					thisOutputStream.close();
				}
			}

			// end
			notifyOperation(Messages.getString("SiteMaker.12"), PROGRESSDISCONNECT); //$NON-NLS-1$
			success = true;
		}
		catch (MalformedURLException thisException)
		{
			notifyOperation(Messages.getString("SiteMaker.20") + theOperation + " [" + thisException.toString() + "]", PROGRESSCOMPLETE); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
		}
		catch (IOException thisException)
		{
			notifyOperation(Messages.getString("SiteMaker.21") + theOperation + " [" + thisException.toString() + "]", PROGRESSCOMPLETE); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
		}
		catch (TransformerConfigurationException thisException)
		{
			notifyOperation(Messages.getString("SiteMaker.22") + theOperation + " [" + thisException.toString() + "]", PROGRESSCOMPLETE); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
		}
		catch (TransformerException thisException)
		{
			notifyOperation(Messages.getString("SiteMaker.23") + theOperation + " [" + thisException.toString() + "]", PROGRESSCOMPLETE); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
		}
		catch (URISyntaxException thisException)
		{
			notifyOperation(Messages.getString("SiteMaker.24") + theOperation + " [" + thisException.toString() + "]", PROGRESSCOMPLETE); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
		}
		finally
		{
			disconnect();
		}
		if (success)
			notifyOperation(Messages.getString("SiteMaker.13"), PROGRESSCOMPLETE); //$NON-NLS-1$
		return success;
	}

	/**
	 * Copy text file to output stream while replacing strings with values
	 * 
	 * @param thisInstream
	 *        input stream
	 * @param thisOutstream
	 *        output strean
	 * @param theseSources
	 *        what to replace
	 * @param theseDestinations
	 *        what to replace it with
	 * @return true if successful
	 */
	private boolean copyStreamsReplace(InputStream thisInstream, OutputStream thisOutstream, String[] theseSources, String[] theseDestinations)
	{
		try
		{
			final int thisBuffercount = 512;
			BufferedReader theReader = new BufferedReader(new InputStreamReader(thisInstream), thisBuffercount);
			PrintWriter theWriter = new PrintWriter(new BufferedOutputStream(thisOutstream));
			String thisLine;
			while ((thisLine = theReader.readLine()) != null)
			{
				for (int i = 0; i < theseSources.length; i++)
				{
					int thisPos = thisLine.indexOf(theseSources[i]);
					if (thisPos != -1)
						thisLine = thisLine.substring(0, thisPos) + theseDestinations[i] + thisLine.substring(thisPos + theseSources[i].length());
				}
				theWriter.println(thisLine);
			}
			theReader.close();
			theWriter.close();
		}
		catch (Exception unused)
		{
			return false;
		}
		return true;
	}

	/**
	 * Copy streams
	 * 
	 * @param thisInstream
	 *        input stream
	 * @param thisOutstream
	 *        output stream
	 * @return true if successful
	 */
	static private boolean copyStreams(InputStream thisInstream, OutputStream thisOutstream)
	{
		try
		{
			final int thisBufferCount = 512;
			BufferedInputStream theReader = new BufferedInputStream(thisInstream, thisBufferCount);
			BufferedOutputStream theWriter = new BufferedOutputStream(thisOutstream);
			byte thisBuffer[] = new byte[thisBufferCount];
			int thisCount;
			while ((thisCount = theReader.read(thisBuffer, 0, thisBufferCount)) != -1)
			{
				theWriter.write(thisBuffer, 0, thisCount);
			}
			theReader.close();
			theWriter.close();
		}
		catch (Exception unused)
		{
			return false;
		}
		return true;
	}
}
